<?php
/**
 * Created by PhpStorm.
 * User: Inhere
 * Date: 15-4-1
 * Time: 上午10:08
 * Used:
 * file: Application.php
 */

namespace ulue\cli;

use Ioc;
use Ulue;
use ulue\base\TheAppException;
use ulue\core\application\AbstractCliApplication;
use ulue\core\ioc\Container;
use ulue\base\providers;
use ulue\core\config\Configuration;
use ulue\web\Module;
use ulue\cli\Controller;

class Application extends AbstractCliApplication
{
    /**
     * a object extends Module
     * @var Module
     */
    protected $module;

    /**
     * a object extends Controller
     * @var Controller
     */
    protected $controller;

    /**
     * @var io\IO
     */
    public $io = null;

    protected $routeVar = 'r';

    /**
     * 是否是调用独立命令 传入参数的 第一个 为 command 关键字
     * @var bool
     */
    protected $isSimple = false;

    public  $defaultModule = 'default';
    public  $basePath  = SRC_PATH;
    public  $debug = false;

    public function __construct( Container $container=null )
    {
        $this->container    = $container instanceof Container ? $container    : Ioc::build();

        // 使用扩展了的 windwalker\IO 组件
        parent::__construct( new io\IO( new io\CliInput ) );

        Ulue::$app  = $this;
        $this->debug = APP_DEBUG;

        Ioc::own()->share('application.io', $this->io)->alias('system.io')->alias('app.io')->alias('io');
    }

    public function init()
    {
        $this->registerServices($this->container);

        $this->configFiles = [
            // 'sys' => dirname(__DIR__) . '/../core/config/data/config.min.inc',
            'app' => COMM_PATH . '/config/config.php',
            'env' => APP_ENV== $this->prodEnvName ? '' : COMM_PATH . '/config/' . APP_ENV . '/config.php',
        ];

        $this->loadConfiguration();

        $this->io->out("\n    *********************************** RUN STARTING *********************************** \n");

        /**
         * @var $logger \Psr\Log\LoggerInterface;
         */
        $logger = $this->container->get('app.logger');

        $this->setLogger($logger);
    }

    /**
     * 载入系统核心服务提供者
     * @return array
     */
    public function loadServiceProviders()
    {
        return [
            'system'      => new providers\SystemServiceProvider($this),      // application && configuration
            'language'    => new providers\LanguageServiceProvider($this->config),
            'environment' => new providers\EnvironmentServiceProvider(),

            // 'dispatcher'  => new providers\DispatcherServiceProvider(),
            'database'    => new providers\DatabaseServiceProvider($this->config),
            // 'cache'       => new providers\CacheServiceProvider,
            'logger'      => new providers\LoggerServiceProvider,
            'session'     => new providers\SessionServiceProvider,

            'router'      => new router\RouterServiceProvider,
        ];
    }


    /**
     * 注册核心共享服务，存在于整个应用运行周期
     * @param Container $container
     */
    public function registerServices(Container $container)
    {
        $providers = $this->loadServiceProviders();

        foreach ($providers as $provider) {
            $container->registerServiceProvider($provider);
        }
    }

    /**
     * 载入应用配置
     * @param Configuration $config
     */
    public function loadConfiguration()
    {
        Ulue::loadConfiguration();
    }

    public function beginExecute()
    {
        $this->parseCliRoute();

        $this->controller->execute();

        $this->debugInformation();
    }

    protected function parseCliRoute()
    {
        $first = $this->io->getRawArg(0);

        // simple command ...
        if ($first === 'command') {
            $this->isSimple = true;
            $second = $this->io->getRawArg(1);
            $routePath = $second{0} != '-' ? $second : $this->io->getOption($this->routeVar);
        } else {
            $routePath = $first{0} != '-' ? $first : $this->io->getOption($this->routeVar);
        }

        /**
         * load router, parse route path
         * @var $router router\Router
         */
        $router  = Ioc::get('application.router', array( trim($routePath, '/\\ ') ) );
        $routeModuleName = $router->get('module');

        // 2.得到模块控制实例
        /**
         * @var $objModule Module
         */
        $objModule       = $this->module = $this->activeModule($routeModuleName, $router);
        $objModule->setRouteController($router);

        // 将应用模块加入共享服务，可在整个应用运行周期内使用 Ioc::get('app.module') 获取实例
        Ioc::share('application.module',function()use($objModule)
        {
            return $objModule;

        })->alias('module')->alias('app.module');

        $routeControllerName  = $router->get('controller');

        /**
         * 3.得到控制器对象实例
         * @var $objController Controller
         */
        $objController = $this->controller = $objModule->activeController($routeControllerName, $this);

        if (!$this->isSimple) {
            $objController->setRouteAction($router);
        }

        // 将应用控制器加入共享服务，可在整个应用运行周期内使用 Ioc::get('app.controller') 获取实例
        Ioc::share('application.controller',function()use($objController)
        {
            return $objController;

        })->alias('controller')->alias('app.controller');
    }

    /**
     * Instantiate the application module  activeModule
     * @param $moduleName
     * @param router\Router $router
     * @throws TheAppException
     * @throws \DRuntimeException
     * @return Module
     */
    protected function activeModule($moduleName, router\Router $router)
    {
        if ( !$moduleName ) {
            throw new TheAppException('访问出现系统错误!模块名丢失！');
        }

        if ( !Module::isModule($moduleName) ) {
            throw new TheAppException(sprintf('访问错误!不存在或未启用模块 [%s] ，请检查。', $moduleName));
        }

        $options     = (array) $this->get("modules.{$moduleName}", array());
        $moduleClass = ucfirst($moduleName). $this->get('suffix.module');

        # 自定义模块目录
        if (!empty($options['class'])) {
            $fullModuleClass = $options['class'];

            if (!empty($options['path'])) {
                Ulue::setNamespace(dirname($fullModuleClass), Ulue::resolveAlias($options['path']) );
            }

        } else {
            $fullModuleClass = $moduleName.'\\'.$moduleClass;
        }

        if ( !class_exists($fullModuleClass) ) {
            throw new \DRuntimeException(sprintf('应用模块 [%s] 入口控制类 [%s] 不存在!!', $moduleName, $fullModuleClass));
        }

        /**
         * @var $objModule \ulue\core\module\AbstractModule
         */
        $objModule  = new $fullModuleClass( $options);
        $class      = new \ReflectionClass($objModule);
        $modulePath = dirname($class->getFileName());

        $objModule->setPath($modulePath)->setName($moduleName)->setRoute('/'.$moduleName);

        $this->configFiles['mod'] = $modulePath . '/resources/config/config.php';
        $objModule->loadConfig($this->configFiles['mod']);

        return $objModule;
    }

    protected function debugInformation()
    {
        if ($this->debug && $c = $this->controller) {

            $file = $c->path . '/' . $c->classname;
            $file = Ulue::getSafePath($file, '<ROOT>');

            $actionInfo = $this->isSimple ? '**' : "** Execute Action: {$c->actionName}{$c->actionSuffix}()";

            echo <<<EOF
\n
    ************************************************************************************
    **                                      :)                                        **
    **                            END OF THE PROGRAM IS RUN                           **
    **                                                                                **
    ************************************************************************************

    ** Debug:       Open
    ** Execute File:
    ** $file.php
    $actionInfo
    **

EOF;
        }
    }

//////////////////////////////////////// ClassLoader ////////////////////////////////////////

    /**
     * @return \ulue\autoload\ClassLoader
     */
    public function getLoader()
    {
        return  $this->container->get('loader');
    }

    /**
     * @param $namespace
     * @param $path
     */
    public function setNamespace($namespace, $path)
    {
        $this->getLoader()->setNs($namespace, $path);
    }

    /**
     * @param $namespace
     * @return bool|string
     */
    public function resolveNamespace($namespace)
    {
        return $this->getLoader()->resolveNs($namespace);
    }

//////////////////////////////////////// property ////////////////////////////////////////

    /**
     * @return Container
     */
    public function getContainer()
    {
        return $this->container;
    }

    /**
     * @param Container $container
     */
    public function setContainer($container)
    {
        $this->container = $container;
    }

    /**
     * @return boolean
     */
    public function getIsSimple()
    {
        return $this->isSimple;
    }

    /**
     * @param boolean $isSimple
     * @return $this
     */
    public function setIsSimple($isSimple)
    {
        $this->isSimple = $isSimple;

        return $this;
    }

    /**
     * @return AbstractModule
     */
    public function getModule()
    {
        return $this->module;
    }

    /**
     * @return Controller
     */
    public function getController()
    {
        return $this->controller;
    }

}// end class ConsoleApplication